package com.woniuxy.ams.shiro;

import com.woniuxy.ams.entity.User;
import com.woniuxy.ams.service.AuthService;
import com.woniuxy.ams.utils.JwtUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author fyxw
 */
public class MyRealm extends AuthorizingRealm {
    private long expireTimeNeedUpdate = 60 * 60;
    private RedisTemplate<String, Object> redisTemplate;
    private AuthService authService;

    public void setExpireTimeNeedUpdate(long expireTimeNeedUpdate) {
        this.expireTimeNeedUpdate = expireTimeNeedUpdate;
    }

    public void setAuthService(AuthService authService) {
        this.authService = authService;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(authService.getRoles(user.getId()));
        return authorizationInfo;
    }

    /**
     * 自定义认证
     * 注意需要重写类中的support方法,或者配置类中使用setAuthenticationTokenClass传入自定义token的类型
     * 否则无法进入认证方法
     *
     * @param token 传入的自定义token
     * @return AuthenticationInfo 用于授权使用
     * @throws AuthenticationException 认证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String jwtToken = (String) token.getPrincipal();
        String username = JwtUtils.getClaim(jwtToken, "username");
        // 从redis中取出对应用户的token
        User user = (User) redisTemplate.opsForValue().get(username + "::token");
        if (user == null || !user.getToken().equals(jwtToken)) {
            throw new AuthenticationException("token认证失败");
        }
        Long expire = redisTemplate.opsForValue().getOperations().getExpire(username + "::token");
        if (expire != null && expire < expireTimeNeedUpdate) {
            // 更新过期时间
            redisTemplate.expire(username + "::token", 120, TimeUnit.MINUTES);
        }
        return new SimpleAuthenticationInfo(user, token.getCredentials(), "myRealm");
    }
}
